
#ifndef _TTG_PIPELINE_PIPELINE_
#define _TTG_PIPELINE_PIPELINE_
#include "Common/Definitions.h"

#include "Common/Structures.h"
#include "Common/Extras.h"
#include "Runtime.Low/Services.h"
#include "Runtime.Low/Utils.h"

#include "BasicPipeline.h"
#include "SubPipeline.h"
#include "Informations/PipelineInfo.h"
#include "Informations/LitePipelineInfo.h"
#include "SubSystem.h"

namespace ttg
{
	namespace pipeline
	{
		//Class, which represents pipeline.
		//It manages arbitary connection of the nodes.
		template <class InType, class OutType>
		class Pipeline :public services::Publisher,
						public ttg_internal::pipeline::BasicPipeline<
									InType,
									OutType,
									ttg_internal::pipeline::StartOfPipeline<InType>,
									ttg_internal::pipeline::EndOfPipeline<OutType> >
		{
			private:
				ttg::structures::Dictionary<ttg_internal::pipeline::ISubPipeline> *sub_pipelines;
				threads::SlowCriticalSection info_cs;			//for synchronization of info accessing.
				bool is_recreated;
#ifdef TTG_PL_VISUALIZER
				int ID;											//ID of publisher.
				ttg::math::uint64 pipelineID;					//ID of pipeline ID in the utilities.
				ttg_internal::pipeline::PipelineInfo *info;
#endif
			public:
				//Creates pipeline for given wrapper.
				//In case of error throws Error().
				Pipeline(NodeWrapper<InType, OutType> &wrapper, const char *name = "DEFAULT_PIPELINE")
					:BasicPipeline<InType, OutType,
								   ttg_internal::pipeline::StartOfPipeline<InType>,
								   ttg_internal::pipeline::EndOfPipeline<OutType>>(wrapper, name)
				{
					//Creating collection for sub-pipelines.
					sub_pipelines
						= ttg::structures::Dictionary<ttg_internal::pipeline::ISubPipeline>::create(35);
					getIContext().setSubPipelines(sub_pipelines);
					is_recreated = false;
#ifdef TTG_PL_VISUALIZER
					//Creating pipeline info.
					this->ID = this->addAvatar("PIPELINE_UPDATE");
					getManager().setTag(0);
					info = new ttg_internal::pipeline::PipelineInfo(name,
																	getManager().getNodes(),
																	&info_cs);
					for (size_t i = 0; i < getManager().getNodes().size(); i++)
					{
						getManager().getNodes()[i]->getInfo().bind(info->getNodesFilter());
					}
					getManager().setTag(0);
					//Registering pipeline info.
					ttg::extras::serialization::ByteSerializer serializer(false);
					ttg_internal::pipeline::LitePipelineInfo linfo(info);
					serializer.serializeObject(&linfo, "pipeline");
					pipelineID
						= ttg_internal::utils::UtilManagerDll::getRef()
							->createPipeline(serializer.getData(), serializer.getSize());
#endif
				}
				//Adds required SubPipeline. It can be accessed by name from any node of pipeline.
				template <class InType, class OutType>
				inline void addSubPipeline(NodeWrapper<InType, OutType> &wrapper, const char *name)
				{
					//Note: created sub-pipeline will register himself in "sub_pipelines".
					//So it's correct (not a memory leak).
					new ttg_internal::pipeline::SubPipeline<InType, OutType>(wrapper,
																			 name,
																			 sub_pipelines,
																			 info->getSubPipelinesFilter());
				}
				inline void performRecreation()
				{
					//Checking.
					if (is_recreated)
						return;
					//Recreating.
					is_recreated = true;
					onPipelineRecreation();
					std::list<ttg_internal::pipeline::ISubPipeline *> en
						= sub_pipelines->toList();
					std::list<ttg_internal::pipeline::ISubPipeline *>::iterator it
						= en.begin();
					for (; it != en.end(); it++)
						dynamic_cast<ttg_internal::pipeline::IPipeline *>(*it)->onPipelineRecreation();
				}
				//Applies user context (replacement for global variables).
				inline void setUserContext(UserContext *context)
				{
					getIContext().getPipelineContext().setUserContext(context);
					std::list<ttg_internal::pipeline::ISubPipeline *> en
						= sub_pipelines->toList();
					std::list<ttg_internal::pipeline::ISubPipeline *>::iterator it = en.begin();
					while (it != en.end())
					{
						(*it)->setUserContext(context);
						it++;
					}
				}
				//Applies user context (replacement for global variables).
				inline void setUserContext(UserContext &context)
				{ setUserContext(&context); }
				//Starts pipeline.
				void start(structures::Enumerator<InType> &data)
				{
					//Preparing.
					getIContext().getPipelineContext().setInputData(getIContext().getRootContextID(), data);
					performRecreation();
					info_cs.enter();
					//Launching.
					ContextID ID = getIContext().getRootContextID();
					startIteration(data, ID);
				}
				//Starts pipeline.
				inline void start(InType data)
				{ start(ttg::structures::ValueEnumerator<InType>(data)); }
				//Starts pipeline.
				inline void start(std::vector<InType> &data)
				{
					start(ttg::structures::StlEnumerator<InType, std::vector<InType>::iterator>
								(data.begin(), data.end()));
				}
				const std::vector<OutType> &finish()
				{
					finishIteration(getIContext().getRootContextID());
					//Synchronizing.
					double t_start = services::ServiceBase::_Timer().getMilliSeconds();
					getManager().getPipelineEnd().getOnFinish().wait();
					double t_elapsed = services::ServiceBase::_Timer().getMilliSeconds() - t_start;
					//Updating informations.
#ifdef TTG_PL_VISUALIZER
					info->update(t_start, t_elapsed, getIContext().getPerformers().createEnumeration());
					for (size_t i = 0; i < getIContext().getPipelineEnumeration().size(); i++)
						getIContext().getPipelineEnumeration()[i]->getInfo()
										.setTimes(t_elapsed, info->getSummedTime());
					this->sendEvent(ID, info);
#endif
					//WTF?
					info_cs.leave();
					getIContext().getDataBuffer().reBuild();
					//Clearing and rebuilding performers.
					getIContext().getPipelineContext().clearInputData(getIContext().getRootContextID());
#ifdef TTG_PL_VISUALIZER
					std::vector<ttg_internal::pipeline::PerformerInfo *> en
						= getIContext().getPerformers().createEnumeration();
					for (size_t i = 0; i < en.size(); i++)
						en[i]->clear();
					getIContext().getPerformers().reBuild();
#endif
					return getManager().getPipelineEnd().getGatheredData();
				}
				//Starts and finishes pipeline.
				const std::vector<OutType> &run(ttg::structures::Enumerator<InType> &data)
				{
					start(data);
					return finish();
				}
				//Starts and finishes pipeline.
				inline const std::vector<OutType> &run(InType data)
				{ return run(ttg::structures::ValueEnumerator<InType>(data)); }
				//Starts and finishes pipeline.
				inline const std::vector<OutType> &run(std::vector<InType> &data)
				{
					return run(ttg::structures::StlEnumerator<InType, std::vector<InType>::iterator>
							(data.begin(), data.end()));
				}
				~Pipeline()
				{
					//Synchronizing.
					ttg_internal::pipeline::NodeState &state
						= getManager().getPipelineEnd().getState(getIContext().getRootContextID());
					while (state.getActiveTaskCount() != 0)
						ttg::threads::CurrentThread::sleep(0.0);
					SAFE_DELETE(sub_pipelines);
					//Avast! Sub pipelines should be released too!
#ifdef TTG_PL_VISUALIZER
					ttg_internal::utils::UtilManagerDll::getRef()->releasePipeline(pipelineID);
					for (size_t i = 0; i < getManager().getNodes().size(); i++)
					{
						getManager().getNodes()[i]->getInfo().unBind();
					}
					SAFE_DELETE(info);
#endif
				}
		};
	}
}

#endif
